package cloud.lysky.java.on.concurrent.thread;

import java.lang.management.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * @author luoly
 * @describe
 * @date 2023/3/1 13:35
 **/
//ManagementFactory用法
public class JvmInfo {
    private static final long MB = 1024 << 10;

    public static void main(String[] args) {
        printAll();
    }

    public static void printAll() {

        //打印系统信息
        System.out.println("===========打印系统信息==========");
        printOperatingSystemInfo();
        //打印编译信息
        System.out.println("===========打印编译信息==========");
        printCompilationInfo();
        //打印类加载信息
        System.out.println("===========打印类加载信息==========");
        printClassLoadingInfo();
        //打印运行时信息
        System.out.println("===========打印运行时信息==========");
        printRuntimeInfo();
        //打印内存管理器信息
        System.out.println("===========打印内存管理器信息==========");
        printMemoryManagerInfo();
        //打印垃圾回收信息
        System.out.println("===========打印垃圾回收信息==========");
        printGarbageCollectorInfo();
        //打印vm内存
        System.out.println("===========打印vm内存信息==========");
        printMemoryInfo();
        //打印vm各内存区信息
        System.out.println("===========打印vm各内存区信息==========");
        printMemoryPoolInfo();
        //打印线程信息
        System.out.println("===========打印线程==========");
        printThreadInfo();
    }


    //输出操作系统信息
    public static void printOperatingSystemInfo() {
        OperatingSystemMXBean system = ManagementFactory.getOperatingSystemMXBean();
        //相当于System.getProperty("os.name")
        System.out.println("系统名称:" + system.getName());
        //相当于System.getProperty("os.version")
        System.out.println("系统版本:" + system.getVersion());
        //相当于System.getProperty("os.arch")
        System.out.println("操作系统架构:" + system.getArch());
        //相当于Runtime.availableProcessors()
        System.out.println("可用内核数:" + system.getAvailableProcessors());
        if (isSunOsMBean(system)) {
            //获取总物理内存
            long totalPhysicalMemorySize = getLongFromOperatingSystem(system, "getTotalPhysicalMemorySize");
            //空闲内存
            long freePhysicalMemorySize = getLongFromOperatingSystem(system, "getFreePhysicalMemorySize");
            //使用的内存
            long usedPhysicalMemorySize = totalPhysicalMemorySize - freePhysicalMemorySize;
            System.out.println("总物理内存(MB):" + totalPhysicalMemorySize / MB);
            System.out.println("已用物理内存(MB):" + usedPhysicalMemorySize / MB);
            System.out.println("剩余总物理内存(MB):" + freePhysicalMemorySize / MB);

            //获取总交换空间
            long totalSwapSpaceSize = getLongFromOperatingSystem(system, "getTotalSwapSpaceSize");
            //已用交换空间
            long freeSwapSpaceSize = getLongFromOperatingSystem(system, "getFreeSwapSpaceSize");
            //剩余交换空间
            long usedSwapSpaceSize = totalSwapSpaceSize - freeSwapSpaceSize;

            System.out.println("总交换空间(M):" + totalSwapSpaceSize / MB);
            System.out.println("已用交换空间(M):" + usedSwapSpaceSize / MB);
            System.out.println("剩余交换空间(M):" + freeSwapSpaceSize / MB);
        }
    }

    public static void printCompilationInfo(){
        CompilationMXBean compilation = ManagementFactory.getCompilationMXBean();
        System.out.println("JIT编译器名称:" + compilation.getName());
        //判断JVM是否支持编译时间的监控
        if (compilation.isCompilationTimeMonitoringSupported()){
            System.out.println("总编译时间:" + compilation.getTotalCompilationTime() + "秒");
        }
    }

    public static void printClassLoadingInfo(){
        ClassLoadingMXBean classLoad = ManagementFactory.getClassLoadingMXBean();
        System.out.println("已加载类总数:" + classLoad.getTotalLoadedClassCount());
        System.out.println("已加载当前类:" + classLoad.getLoadedClassCount());
        System.out.println("已卸载类总数" + classLoad.getUnloadedClassCount());
    }

    public static void printRuntimeInfo(){
        RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
        System.out.println("进程PID1:" + runtime.getName().split("@")[0]);
//        System.out.println("进程PID2:" + runtime.getPid());
        System.out.println("JVM规范名称:" + runtime.getSpecName());
        System.out.println("JVM规范运营商:" + runtime.getSpecVendor());
        System.out.println("JVM规范版本:" + runtime.getSpecVersion());
        //返回虚拟机在毫秒内的开机时间，该方法返回了虚拟机启动时的近似时间
        System.out.println("JVM启动时间(毫秒):" + runtime.getStartTime());
        //相当于System.getProperties()
        System.out.println("获取System.properties:" + runtime.getSystemProperties());
        System.out.println("JVM正常运行时间(毫秒):" + runtime.getUptime());
        //相当于System.getProperty(java.vm.name)
        System.out.println("JVM名称:" + runtime.getVmName());
        //相当于System.getProperty("java.vm.vendor")
        System.out.println("JVM运营商:" + runtime.getVmVendor());
        //相当于System.getProperty("java.vm.version")
        System.out.println("JVM实现版本:" + runtime.getVmVendor());
        List<String> inputArguments = runtime.getInputArguments();
        if (inputArguments != null && !inputArguments.isEmpty()){
            System.out.println("VM参数:");
            for (String inputArgument : inputArguments) {
                System.out.println(inputArgument);
            }
        }
        System.out.println("类路径:" + runtime.getClassPath());
        System.out.println("引导类路径:" + runtime.getBootClassPath());
        System.out.println("库路径:" + runtime.getLibraryPath());
    }

    public static void printMemoryManagerInfo(){
        List<MemoryManagerMXBean> managers = ManagementFactory.getMemoryManagerMXBeans();
        if (managers != null && !managers.isEmpty()){
            for (MemoryManagerMXBean manager : managers) {
                System.out.println("VM内存管理器: 名称=" + manager.getName() +
                        "管理的内存区=" + Arrays.deepToString(manager.getMemoryPoolNames()) + ",ObjectName=" + manager.getObjectName());
            }
        }
    }

    public static void printGarbageCollectorInfo(){
        List<GarbageCollectorMXBean> garbages = ManagementFactory.getGarbageCollectorMXBeans();
        for (GarbageCollectorMXBean garbage : garbages) {
            System.out.println("垃圾收集器：名称="+garbage.getName()+",收集="+garbage.getCollectionCount()+",总花费时间="
                    +garbage.getCollectionTime()+",内存区名称="+Arrays.deepToString(garbage.getMemoryPoolNames()));
        }
    }

    public static void printMemoryInfo(){
        MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
        MemoryUsage headMemory = memory.getHeapMemoryUsage();
        System.out.println("head堆:");
        System.out.println("\t初始(M):"+headMemory.getInit()/MB);
        System.out.println("\t最大(上限)(M):"+headMemory.getMax()/MB);
        System.out.println("\t当前(已使用)(M):"+headMemory.getUsed()/MB);
        System.out.println("\t提交的内存(已申请)(M):"+headMemory.getCommitted()/MB);
        System.out.println("\t使用率:"+headMemory.getUsed()*100/headMemory.getCommitted()+"%");

        System.out.println("non-head非堆:");
        MemoryUsage nonheadMemory = memory.getNonHeapMemoryUsage();
        System.out.println("\t初始(M):"+nonheadMemory.getInit()/MB);
        System.out.println("\t最大(上限)(M):"+nonheadMemory.getMax()/MB);
        System.out.println("\t当前(已使用)(M):"+nonheadMemory.getUsed()/MB);
        System.out.println("\t提交的内存(已申请)(M):"+nonheadMemory.getCommitted()/MB);
        System.out.println("\t使用率:"+nonheadMemory.getUsed()*100/nonheadMemory.getCommitted()+"%");
    }

    public static void printMemoryPoolInfo(){
        List<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans();
        if(pools != null && !pools.isEmpty()){
            for(MemoryPoolMXBean pool : pools){
                //只打印一些各个内存区都有的属性，一些区的特殊属性，可看文档或百度
                //最大值，初始值，如果没有定义的话，返回-1，所以真正使用时，要注意
                System.out.println("vm内存区:\n\t名称="+pool.getName()+"\n\t所属内存管理者="+Arrays.deepToString(pool.getMemoryManagerNames())
                        +"\n\t ObjectName="+pool.getObjectName()+"\n\t初始大小(M)="+pool.getUsage().getInit()/MB
                        +"\n\t最大(上限)(M)="+pool.getUsage().getMax()/MB
                        +"\n\t已用大小(M)="+pool.getUsage().getUsed()/MB
                        +"\n\t已提交(已申请)(M)="+pool.getUsage().getCommitted()/MB
                        +"\n\t使用率="+(pool.getUsage().getUsed()*100/pool.getUsage().getCommitted())+"%");
            }
        }
    }

    public static void printThreadInfo(){
        ThreadMXBean thread = ManagementFactory.getThreadMXBean();
        System.out.println("ObjectName="+thread.getObjectName());
        System.out.println("仍活动的线程总数="+thread.getThreadCount());
        System.out.println("峰值="+thread.getPeakThreadCount());
        System.out.println("线程总数（被创建并执行过的线程总数）="+thread.getTotalStartedThreadCount());
        System.out.println("当初仍活动的守护线程（daemonThread）总数="+thread.getDaemonThreadCount());

        //检查是否有死锁的线程存在
        long[] deadlockedIds =  thread.findDeadlockedThreads();
        if(deadlockedIds != null && deadlockedIds.length > 0){
            ThreadInfo[] deadlockInfos = thread.getThreadInfo(deadlockedIds);
            System.out.println("死锁线程信息:");
            System.out.println("\t\t线程名称\t\t状态\t\t");
            for(ThreadInfo deadlockInfo : deadlockInfos){
                System.out.println("\t\t"+deadlockInfo.getThreadName()+"\t\t"+deadlockInfo.getThreadState()
                        +"\t\t"+deadlockInfo.getBlockedTime()+"\t\t"+deadlockInfo.getWaitedTime()
                        +"\t\t"+deadlockInfo.getStackTrace().toString());
            }
        }
        long[] threadIds = thread.getAllThreadIds();
        if(threadIds != null && threadIds.length > 0){
            ThreadInfo[] threadInfos = thread.getThreadInfo(threadIds);
            System.out.println("所有线程信息:");
            System.out.println("\t\t线程名称\t\t\t\t\t状态\t\t\t\t\t线程id");
            for(ThreadInfo threadInfo : threadInfos){
                System.out.println("\t\t"+threadInfo.getThreadName()+"\t\t\t\t\t"+threadInfo.getThreadState()
                        +"\t\t\t\t\t"+threadInfo.getThreadId());
            }
        }

    }

    private static long getLongFromOperatingSystem(OperatingSystemMXBean operatingSystem, String methodName) {
        try {
            final Method method = operatingSystem.getClass().getMethod(methodName, (Class<?>[]) null);
            method.setAccessible(true);
            return (Long) method.invoke(operatingSystem, (Object[]) null);
        } catch (NoSuchMethodException | IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            if (e.getCause() instanceof Error) {
                throw (Error) e.getCause();
            } else if (e.getCause() instanceof RuntimeException) {
                throw (RuntimeException) e.getCause();
            }
            throw new IllegalArgumentException(e.getCause());
        }
    }

    private static boolean isSunOsMBean(OperatingSystemMXBean operatingSystem) {
        final String className = operatingSystem.getClass().getName();
        return "com.sun.management.OperatingSystem".equalsIgnoreCase(className) || "com.sun.management.UnixOperatingSystem".equalsIgnoreCase(className);
    }
}
